﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Diagnostics;

namespace Bettzueche.RLGlue.Codec.Installer {

    /// <summary>
    /// NICHT GETESTET. (Und wahrsch. auch nicht nötig)
    /// </summary>
    /// <remarks>
    /// This class can be used to check the installation status of the RL-Glue
    /// Java Extension,  It can install or uninstall that codec also.
    /// @author btanner
    /// </remarks>
    class Installer {

        private static readonly String _alreadyInstErrorText = 
            " -- PROBLEM --" + Environment.NewLine +
            "You are presumably trying to install the RL-Glue" +
            "Extension from a JAR file that you have downloaded.  You already " +
            "have a version of this extension installed on this computer." + Environment.NewLine + Environment.NewLine +
            "The current version is: " + RLGlueCore.GetSpecVersion() + " :: " + RLGlueCore.GetImplementationVersion() +
            "Because of the way that Java handles class-loading order of extensions, you will have to " + 
            "remove the old one before installing the presumably newer one.  I (the one writing this) am the old one." +
            Environment.NewLine + Environment.NewLine +
            "To remove me, try:" + Environment.NewLine + 
            " java org.rlcommunity.rlglue.codec.RLGlueCore --uninstall";

        private static readonly String _installNotification =
            "This program will install the RL-Glue Java " +
            "extension " +
            "into one of your system extension directories.  This may " +
            "require superuser or root access to do.  Also, depending on the " +
            "location you choose, you may need to " +
            "reinstall if you update your VM." + 
            Environment.NewLine + Environment.NewLine +
            "You should be aware that there are some "+
            "disadvantages to installing the codec instead of just manually putting it into " +
            "you java class path.  These are explained in the manual.";

        /// <summary>Diese Assembly-Datei</summary>
        private static readonly FileInfo _thisAssemblyFile;

        static Installer() {
            try {
                _thisAssemblyFile = new FileInfo(Assembly.GetAssembly(typeof(Installer)).Location);
            }
            catch (Exception) {
                _thisAssemblyFile = null;
            }
        }

        /// <summary>
        /// FloatDir??? Pfad zu dieser Assembly-Datei.
        /// </summary>
        /// <returns>Vollständiger Pfad zu dieser Assembly-Datei</returns>
        /// <exception cref="RLGlueException">This class was loaded from an installed location.</exception>
        [Obsolete("Will be replaced by alternative Install Methods soon")]
        public static String GetFloatDir() {
            if (!IsInstalled()) {
                throw new RLGlueException("This class was loaded from an installed location.");
            }
            FileInfo theJar = _thisAssemblyFile;
            if (theJar == null) {
                return "unknown";
            } else {
                return theJar.FullName;
            }
        }

        /// <summary>
        /// FloatDir??? Pfad zu dieser Assembly-Datei.
        /// </summary>
        /// <returns>Vollständiger Pfad zu dieser Assembly-Datei</returns>
        /// <exception cref="RLGlueException">This class was NOT loaded from an installed location.</exception>
        [Obsolete("Will be replaced by alternative Install Methods soon")]
        public static String GetInstallDir() {
            Trace.Assert(IsInstalled(), "This class was not loaded from an installed location.");
            if (!IsInstalled()) {
                throw new RLGlueException("This class was not loaded from an installed location.");
            }
            return getThisAssemblyFile().FullName;
            //return getThisJar().getAbsolutePath();
        }

        /// <summary>
        /// Tut nix.<para>
        /// Wird demnächst durch VS10-Installationsmethoden ersetzt!</para>
        /// </summary>
        [Obsolete("Will be replaced by alternative Install Methods soon")]
        public static void Install() {
            //// bereits installiert? -> Abbruch
            //if (IsInstalled()) {
            //    Trace.TraceError(_alreadyInstErrorText);
            //    Environment.Exit(1);
            //}
            //// Assembly nicht vorhanden? -> Abbruch
            //if (_thisAssemblyFile == null) {
            //    Trace.TraceError("Could not get the Assembly that you are " +
            //            "executing from, aborting.");
            //    Environment.Exit(1);
            //}

            //Console.WriteLine(_installNotification);

            //Console.WriteLine();
            //Console.WriteLine("Possible extension directories to install to:");
            //bool anyWriteable = false;
            //bool anyNotWriteable = false;

            //ISet<FileInfo> allDirs = getExtensionDirs();

            //ISet<FileInfo> writeableChoices = new HashSet<FileInfo>();
            //foreach (FileInfo thisDirFile in allDirs) {

            //    if (thisDirFile.canWrite()) {
            //        Console.WriteLine(" WRITEABLE     :: ");
            //        writeableChoices.Add(thisDirFile);
            //    } else {
            //        Console.WriteLine(" NOT WRITEABLE :: ");
            //    }

            //    Console.WriteLine(thisDirFile.FullName);
            //    anyWriteable |= thisDirFile.canWrite();
            //    anyNotWriteable |= !thisDirFile.canWrite();
            //}

            //if (!anyWriteable) {
            //    Console.WriteLine("None of the choices were writeable. " +
            //            "You will have to execute as root or with sudo to install.");
            //} else {
            //    if (anyNotWriteable) {
            //        Console.WriteLine("Some of the choices were not writeable. " +
            //                "You will have to execute as root or with sudo to install there.");
            //    }
            //}

            //FileInfo theChoice = null;
            //if (anyWriteable) {
            //    try {
            //        Dictionary<Int32, FileInfo> choiceMap = new Dictionary<int,FileInfo>()/* new TreeMap<Integer, File>()*/;
            //        Console.WriteLine();
            //        Console.WriteLine("If you would like to proceed, select a location to install:");
            //        int counter = 1;
            //        Console.WriteLine("0. Cancel Installation (Quit)");
            //        foreach (FileInfo file in writeableChoices) {
            //            choiceMap[counter] = file;
            //            Console.WriteLine(counter + ".  " + file.FullName);
            //        }
            //        Console.WriteLine("Your selection: ");

            //        int theIntChoice = 0;

            //        try {
            //            theIntChoice = ConsoleReader.readInt();
            //        } catch (Exception) {
            //            //Errors will fall through and cause us to quit.
            //        }
            //        Console.WriteLine();
            //        if (choiceMap.ContainsKey(theIntChoice)) {
            //            theChoice = choiceMap[theIntChoice];
            //        } else {
            //            Trace.TraceError("You did not enter a valid selection or chose to quit, cancelling installation.");
            //            Environment.Exit(1);
            //        }


            //        FileInfo DestinationFile = new FileInfo(theChoice, theJarFile.Name);
            //        Console.WriteLine();
            //        Console.WriteLine(theJarFile.FullName + " ==> " + DestinationFile.FullName);
            //        if (DestinationFile.Exists) {
            //            Console.WriteLine("Removing existing version first...");
            //            DestinationFile.Delete();
            //        }
            //        CopyFileTo(theJarFile, DestinationFile);
            //        Console.WriteLine("Installation Complete!");
            //        Console.WriteLine("Test it by typing:");
            //        Console.WriteLine(">$ java org.rlcommunity.rlglue.codec.RLGlueCore --version");
            //    } catch (IOException ex) {
            //        Trace.TraceError(typeof(Installer).Name + ": \n" + ex.StackTrace);
            //    }
            //}
        }


        /// <summary>
        /// Returns true of this class was loaded from an installed location.
        /// </summary>
        /// <returns>Derzeit Immer false! (true, if this class was loaded from an installed location.)</returns>
        /// <remarks>Überprüft in Java-Version, ob JavaRLGlueCodec.jar im java-lib-ext Verzeichnis liegt.
        /// Das gilt dann als lokal installiert.</remarks>
        [Obsolete("Will be replaced by alternative Install Methods soon")]
        public static bool IsInstalled() {
            if (_thisAssemblyFile == null) {
                return false;
            }
            ISet<DirectoryInfo> allExtensionDirs = getExtensionDirs();
            return allExtensionDirs.Contains(_thisAssemblyFile.Directory);
        }


        /// <summary>
        /// Achtung! Führt derzeit noch zu Programmabbruch.
        /// </summary>
        /// <remarks>
        /// Java-Beschreibung von Brian Tanner:
        /// This will make sure that the codec is installed somewhere in the
        /// extensions path,and if so, it will remove the jar.
        /// </remarks>
        [Obsolete("Will be replaced by alternative Install Methods soon")]
        public static void Uninstall() {
            if (!IsInstalled()) {
                Console.WriteLine("You cannot uninstall the RL-Glue .NET Extension if " +
                        "it has not been formally \"installed\" on your system.  From " +
                        "what I can detect, you loaded the RL-Glue codec from: " + Environment.NewLine + _thisAssemblyFile.FullName);
                Environment.Exit(1);
            }
            FileInfo theJarFile = getThisAssemblyFile();
            // Wozu folgender Krams? :
            //if (!_thisAssemblyFile.Name.Equals("JavaRLGlueCodec.jar")) {
            //    Console.WriteLine("I will not uninstall because the jar that RL-Glue" +
            //            "is in appears to be part of a redistribution.  Deleting" +
            //            "a jar that is not called \"JavaRLGlueCodec.jar\" might" +
            //            "make you mad.  The jar that I was loaded from is: \n" + getThisAssemblyFile());
            //    Environment.Exit(1);
            //}
            Console.WriteLine("Removing RL-Glue .NET Extension from:"+ Environment.NewLine + _thisAssemblyFile.FullName);
            try {
                _thisAssemblyFile.Delete();
                Console.WriteLine("Uninstallation Complete!");
            }
            catch (Exception) {
                Console.WriteLine("Error: Could not delete the file.  Try again with sudo or root access.");
            }
        }

        /// <summary>
        /// Java extension aus xxx\jre\lib\ext und mögliche weitere Pfade. (Wozu?)
        /// <para>
        /// Anscheinend wird die Java RLGlue in \lib\ext installiert.
        /// Installer.IsInstalled überprüft, ob die JavaRLGlueCodec.jar im Lib-Extensions Verzeichnis
        /// vorhanden ist.
        /// </para>
        /// </summary>
        /// <returns>Leeres Set. (Gibt Ordnerpfade zu Java-Ext Verzeichnissen.)</returns>
        [Obsolete("Will be replaced by alternative Install Methods soon")]
        private static ISet<DirectoryInfo> getExtensionDirs() {
            //String extensionDirs = System.getProperty("java.ext.dirs");
            //String[] allDirsArray = extensionDirs.Split(Path.PathSeparator);
            ISet<DirectoryInfo> allDirSet = new HashSet<DirectoryInfo>();

            //foreach (String thisFileName in allDirsArray) {
            //    allDirSet.Add(new DirectoryInfo(thisFileName));
            //}
            return allDirSet;
        }


        /// <summary>
        /// Gibt diese Assembly-Datei.
        /// </summary>
        /// <returns>Diese Assembly-Datei</returns>
        [Obsolete("Will be replaced by alternative Install Methods soon")]
        private static FileInfo getThisAssemblyFile() {
            return _thisAssemblyFile;
        }
        //private static File getThisJar() {
        //    File theJarFile = null;
        //    URL codeBase = Installer.class.getProtectionDomain().getCodeSource().getLocation();
        //    if (codeBase.getPath().endsWith(".jar")) {
        //        try {
        //            theJarFile = new File(codeBase.toURI());
        //        } catch (URISyntaxException ex) {
        //            Logger.getLogger(Installer.class.getName()).log(Level.SEVERE, null, ex);
        //        }
        //    }
        //    return theJarFile;
        //}


        /// <summary>
        /// Copy the source file to the target file.  If the destination file does not exist, it is created.
        /// </summary>
        /// <param name="sourceFile"></param>
        /// <param name="target"></param>
        /// <exception cref="RLGlueException"></exception>
        public static void CopyFileTo(FileInfo sourceFile, FileInfo target) {
            Trace.Assert(sourceFile != null);
            Trace.Assert(target != null);
            Trace.Assert(sourceFile.Exists,"File we are copying from (" + sourceFile.FullName + ") does not exist in copyFileTo");
            Trace.Assert(sourceFile != target);
            try {
                File.Copy(sourceFile.FullName,target.FullName);
            }
            catch (Exception ex) {
                Trace.TraceError("Installer.CopyFileTo Error: {0}",ex.Message);
                throw new RLGlueException("Installer.CopyFileTo Error.",ex);
            }
        }
    }
}
